Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: 3D Renderer and Preview Export of Thumbnails #87

Merged
merged 43 commits into from
Aug 27, 2024

Conversation

rahelarnold98
Copy link
Contributor

This pull request integrates a 3D model renderer and introduces a 3D model preview exporter.

The renderer, originally developed by @net-cscience-raphael for Cineast using LWJGL, retains its core functionality while the texture model has been translated to Kotlin for enhanced integration. This updated version replaces the previous implementation in the vitrivr-engine-core module.

In addition, new functionality has been added to create previews of 3D models in the vitrivr-engine-module-m3d/src/main/kotlin/org/vitrivr/engine/model3d/ModelPreviewExporter.kt. By specifying settings in the schema-config.json file, users can export either GIFs or JPGs that showcase 3D models. GIFs allow for a customizable number of views capturing different angles of the 3D model, while the JPG version provides four predefined views. To ensure correct formatting of exported previe ws, the mimeType of the DiskResolver must be adjusted in the config file.

Overview of new Functionality:

  • Integration of a 3D model renderer
  • Export of GIF previews for 3D models
  • Export of JPG previews for 3D models

@rahelarnold98 rahelarnold98 marked this pull request as ready for review July 12, 2024 09:19
@rahelarnold98 rahelarnold98 self-assigned this Jul 12, 2024
@ppanopticon
Copy link
Member

Thanks for the PR.

In order to be able to review this it would be nice if you could add some minimal working example / documentation snippet as to how this can be used and tested.

Furthermore, I was wondering if it would be possible to have a minimal UnitTest that makes sure that rendering works. See PR #84 for further reference.

@ppanopticon ppanopticon changed the title Feature: 3D Renderer and Preveiw Export of Thumbnails Feature: 3D Renderer and Preview Export of Thumbnails Jul 26, 2024
@ppanopticon
Copy link
Member

ppanopticon commented Jul 26, 2024

In addition to the detailed comments and the previous remark, I have two more fundamental issue with this PR and the way the M3D module is setup:

  1. I don't think it's particularly wise to add Java code to a Kotlin project, unless there is a good reason to (which I don't see).
  2. I was wondering if the JOML dependency in the vitrivr-engine-core module could be removed by using our own datatypes and convert them, once Vector3f are actually needed.

In its current state, the PR seems to violate some basic principles of the project (e.g., the ability to include or omit certain modules). Furthermore, it seems to be a bit messy since it's littered with empty classes and deprecated code.

@ppanopticon ppanopticon requested a review from lucaro July 26, 2024 08:04
Copy link
Member

@lucaro lucaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pull request is clearly not ready. It contains lots of Java code that has no place in a kotlin code base (especially not under src/main/kotlin), including various deprecated methods, interfaces, and classes. The rendering mechanism is needlessly complicated and does not adhere to any of the architectural conventions we introduced in this project. The way this is now, this looks not maintainable. Some substantial rework is required before this can be merged.

* position and scale the model in the scene. The [Material] objects are used to define the
* appearance of the model.
*/
data class Model(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This, again, is a very generic name, as it could 'model' anything. I suggest renaming it to something more descriptive.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed model to model3d to specify that a 3d model is used

@@ -117,7 +117,7 @@ class CachedContentFactory : ContentFactoriesFactory {
return content
}

override fun newMeshContent(model3D: Model3D): Model3DContent {
override fun newMeshContent(model: Model): ModelContent {
check(!this.closed) { "CachedContentFactory has been closed." }
TODO()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be properly implemented before merging this PR.


/**
* A 3D [ContentElement].
*
* @author Rahel Arnold
* @version 1.0.0
*/
interface Model3DContent: ContentElement<Model3D>{
/** The [ContentType] of a [Model3DContent] is always [ContentType.MESH]. */
interface ModelContent: ContentElement<Model>{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ModelContent is very generic; the previous name was more descriptive. I'd change it back.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done👍

private val modelId: String
) {
private val LOGGER = LogManager.getLogger()
class Entity(val id: String, val modelId: String) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This, again, is a very generic name. I'm sure there is a more descriptive one.


import org.joml.Vector3f

/**
*
*/
interface IModel {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm personally not a big fan of the I-prefix naming scheme for interfaces. It usually indicates that there is another thing that should have a more specific name.

* Used to indicate that a key is not valid.
*/
@SuppressWarnings("unused")
public class VariantException extends RuntimeException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

???

* If a job is a CONTROL job it contains a command
* {@link JobControlCommand}
*/
public abstract class Job {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why this is needed

* This class is used to parse the annotations of a state provider
* @see StateProviderAnnotationParser#runTransitionMethods(Object, State, State, Transition, Variant)
*/
public class StateProviderAnnotationParser {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very much not the kotlin way of thigs

* @see <a href="https://stackoverflow.com/questions/5923767/simple-state-machine-example-in-c">
* https://stackoverflow.com/questions/5923767/simple-state-machine-example-in-c/a>
*/
public class FiniteStateMachine {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WHY?!

val renderWorker = RenderWorker(renderJobQueue)

/* Run RenderWorker on the main thread. */
renderWorker.run()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

@rahelarnold98
Copy link
Contributor Author

Thank you for the detailed review @lucaro. I will work through the comments and make the adjustments.

Nevertheless, I think we should discuss whether the complete rendering logic that uses LWJGL must be translated to Kotlin or if we can include this independent Java code. The code was directly copied from Cineast, so the finite state machine was implemented like this before.

Furthermore, I have not found a Java class in src/main/kotlin. Please point them out. For me, all Java code is located under src/main/java.

@ppanopticon Thank you as well for your feedback. I will add an example and unit tests and rewrite some parts to remove the JOML dependency in the core module.

@lucaro
Copy link
Member

lucaro commented Jul 29, 2024

I'm aware that this was implemented like this in Cineast. It was over-engineered there (as I already pointed out back then), and it is over-engineered here. Here, it's even worse since Cineast had already collected some bloat over the years, so the addition was relatively not as bad. I'm also opposed to supporting multiple languages in a project with no clear benefit. This just complicates maintenance and reduces readability in the long run. Translating the Java code to Kotlin is straightforward and even (mostly) automated by Intellij. While translating this (or, mostly, having it automatically translated), there will also be room to get rid of some inherent boilerplate code that is just not required in Kotlin.
You are right about the package structure, the Java classes all appear to be in src/main/java, my bad.

@ppanopticon
Copy link
Member

ppanopticon commented Jul 29, 2024

Okay so I have taken another look at this, especially how this can be integrated without interfering with the flow of the integrating application. Having spent half a day on this I can conclude, that the only way we can use the rendering logic without hooking it into the application's main thread is by externalising it into a different process.

For this purpose, I have created the Renderer and the ExternalRenderer implementation, which are basically two sides of a coin: The Renderer is a small Java application (basically a main() method) that reads RenderRequests from a an ObjectInputStream, processes them and writes them to the ObjectOutputStream. The ExternalRenderer spawns such a process, generates and sendsRenderRequests and processes incoming RenderResponses. The very simple example seems to work. However, there is an issue in ModelPreviewExporter.renderPreviewJPEG that makes the unit test fail (I'll add a comment).

Having said that: I agree with @lucaro that the entire rendering logic (i.e., this state-machine stuff) is way too complicated for solving a simple problem such as rendering a few previews. I really wonder, if this can't be simplified. To me, the fact that the entire state-machine logic in Worker is basically a Runnable (which is typically supposed to run in a different thread) and that GLFW must be executed on the main thread is a clear sign that the solution and the problem do not fit together.

@rahelarnold98
Copy link
Contributor Author

The JOML dependency in the vitriol-engine-core module is now removed.
Concerning documentation, I will add the instructions in the Wiki.

@rahelarnold98
Copy link
Contributor Author

An example of how the configuration should look like to create previews can already be found in the Wiki: https://github.com/vitrivr/vitrivr-engine/wiki/Example#3d-model-pipeline

@ppanopticon ppanopticon requested review from lucaro and removed request for net-cscience-raphael August 19, 2024 07:27
rahelarnold98 and others added 16 commits August 22, 2024 13:52
Signed-off-by: Ralph Gasser <[email protected]>
Signed-off-by: Ralph Gasser <[email protected]>
Signed-off-by: Ralph Gasser <[email protected]>
Signed-off-by: Ralph Gasser <[email protected]>
Signed-off-by: Ralph Gasser <[email protected]>
@ppanopticon ppanopticon added the enhancement New feature or request label Aug 26, 2024
@ppanopticon ppanopticon requested review from ppanopticon and removed request for ppanopticon August 26, 2024 08:41
}

/* Initialize RenderWorker on the main thread. */
val renderJobQueue = LinkedBlockingDeque<RenderJob>()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this to the vitrivr-engine-server like this goes against the modularity idea of vitrivr-engine (i.e., the ability to actually omit this module in a concrete project).

@@ -7,6 +7,7 @@ plugins {
dependencies {
/* vitrivr core dependency. */
api project(':vitrivr-engine-core')
api project(':vitrivr-engine-module-m3d')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The vitrivr-engine-index module must no depend on vitrivr-engine-module-m3d. This makes no sense from an architectural perspective.

If anything, the vitrivr-engine-module-m3d can be added to the vitrivr-engine-module-server for testing purposes only.


import java.util.Objects;

public class Pair<K, V> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this was Kotlin code, then we wouldn't need to have this class.

@@ -0,0 +1,2 @@
package org.vitrivr.engine.model3d.data.render.lwjgl;public class Renderer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this class?

* - Rendering of single Mesh or VoxelGrid - Free positioning of the camera in terms of either cartesian or polar coordinate - Snapshot of the rendered image can be obtained at any time.
* @deprecated This interface is deprecated it can be simplified if the old JOGL renderer is removed.
*/
@Deprecated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add this to the project if it is already deprecated?

@rahelarnold98
Copy link
Contributor Author

@lucaro This pull request is ready to merge from my side. Could you please review it once more?

@lucaro
Copy link
Member

lucaro commented Aug 26, 2024

If @ppanopticon gave his approval, it's good enough for me 😉

@rahelarnold98 rahelarnold98 merged commit 9fee7e5 into dev Aug 27, 2024
2 checks passed
@Spiess Spiess deleted the feature/3d-preview branch October 18, 2024 06:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants